const TYPE_ENUM = {
  '[object Number]': 'number',
  '[object String]': 'string',
  '[object Boolean]': 'boolean',
  '[object Array]': 'array',
  '[object Object]': 'object'
}

const validatorType = (value) => TYPE_ENUM[Object.prototype.toString.call(value)]

function isValidator (key, type, realityType) {
  let t = type ?? 'string'
  if (validatorType(type) === 'string') {
    t = [t]
  }
  t = new Set(t.map(v => v.toLowerCase()))
  if (!t.has(realityType)) {
    throw new Error(`${key}: 预期类型 ${type}与实际类型${realityType}不符`)
  }
}

function requiredValidator (key, value, rule) {
  const v = value ?? rule.default
  if (v !== undefined) return v // 当前属性值有值则不需要校验必填
  if (!rule.required) return v // 如果匹配规则非必填 则可直接跳过校验
  throw new Error(`${key}: 为必填项`)
}

export function validator (params, validatorRule) {
  for (const key in validatorRule) {
    if (Object.hasOwnProperty.call(validatorRule, key)) {
      const rule = validatorRule[key]
      params[key] = requiredValidator(key, params[key], rule)
      const type = validatorType(params[key])
      if (params[key] !== undefined) {
        isValidator(key, rule.type, type)
      } else {
        delete params[key]
      }
      if (type === 'array' && rule.items) {
        for (const item of params[key]) {
          validator(item, rule.items)
        }
      }
      if (type === 'object' && rule.properties) {
        validator(params[key], rule.properties)
      }
    }
  }
  return params
}

// ------------------------------
const validatorRule = {
  id: { type: 'NUMBER', required: true },
  isRemoved: { type: 'BooLEAN', required: true, default: false },
  name: { type: 'string', required: true },
  arr: {
    type: 'array',
    items: {
      id: { type: 'string', required: true, default: '1' }
    }
  },
  obj: {
    type: 'Object',
    properties: {
      num: { type: 'number', required: true }
    }
  }
}

const params = {
  id: 2,
  name: '张三',
  arr: [{ id: '1' }, { id: '2' }, {}],
  obj: { num: 1 }
}

console.log(validator(params, validatorRule))
